package hos.http;

import android.app.Activity;
import android.app.Dialog;

import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import hos.http.converter.HttpConvert;

/**
 * <p>Title: HttpRequest </p>
 * <p>Description:  </p>
 * <p>Company: www.mapuni.com </p>
 *
 * @author : 蔡俊峰
 * @version : 1.0
 * @date : 2023-02-23 14:42
 */
@SuppressWarnings("unchecked")
public class HttpRequest<T extends HttpRequest<T>> implements HttpBaseRequest<T> {

    private String url;

    protected String method = HttpMethod.GET;
    /**
     * 请求体的媒体类型
     */
    protected String mediaType = HttpContentType.URLENCODED;
    /**
     * 请求拦截器
     */
    private final List<HttpInterceptor.RequestInterceptor> requestInterceptor = new ArrayList<>();
    /**
     * 响应拦截器
     */
    private final List<HttpInterceptor.ResponseInterceptor> responseInterceptor = new ArrayList<>();
    /**
     * 异常拦截器
     */
    private final List<HttpInterceptor.ExceptionInterceptor> exceptionInterceptor = new ArrayList<>();

    /**
     * 下载请求
     */
    private final HttpTag.DownloadListeners downloadProgressListeners = new HttpTag.DownloadListeners();
    /**
     * 结果转换器
     */
    private HttpConvert<?> httpConvert;

    private final Map<String, String> header = new HashMap<>();

    private final List<HttpParam> bodyParameters = new ArrayList<>();
    private final List<HttpParam> queryParameters = new ArrayList<>();

    /// 是否重试
    private int retryCount;

    private Integer connectTimeout;
    private Integer readTimeout;
    private Integer writeTimeout;
    private Type type;

    private boolean showDialog = false;

    private Dialog dialog;

    public HttpRequest() {
        init();
    }

    public T apply(HttpApply<T> httpApply) {
        httpApply.apply((T) this);
        return (T) this;
    }

    @Override
    public T dialog(boolean showDialog, Activity activity, HttpDialogFactory dialogFactory) {
        this.showDialog = showDialog;
        if (dialogFactory == null) {
            dialogFactory = config().getDialogFactory();
        }
        if (showDialog && dialogFactory != null) {
            dialog = dialogFactory.create(activity);
        } else {
            dialog = null;
        }
        return (T) this;
    }

    @Override
    public T dialog(boolean showDialog, Activity activity, Dialog dialog) {
        this.showDialog = showDialog;
        if (showDialog) {
            this.dialog = dialog;
        } else {
            this.dialog = null;
        }
        return (T) this;
    }

    @Override
    public T dialog(boolean showDialog, Activity activity) {
        this.showDialog = showDialog;
        HttpDialogFactory dialogFactory = config().getDialogFactory();
        if (showDialog && dialogFactory != null) {
            if (HttpConfig.isMainThread()) {
                dialog = dialogFactory.create(activity);
            } else {
                HttpConfig.runUI(() -> dialog = dialogFactory.create(activity));
            }
        } else {
            dialog = null;
        }
        return (T) this;
    }

    @Override
    public T init() {
        clear();
        check();
        return (T) this;
    }

    public T contentType(String contentType) {
        if (contentType == null || contentType.length() == 0) {
            return (T) this;
        }
        this.mediaType = contentType;
        return (T) this;
    }

    public String getContentType() {
        return mediaType;
    }

    public T method(String method) {
        this.method = method;
        return (T) this;
    }

    public String getMethod() {
        if (HttpMethod.DOWNLOAD.equals(method)) {
            return HttpMethod.GET;
        }
        return method;
    }


    public T url(String url) {
        this.url = url;
        return (T) this;
    }

    public String getUrl() {
        return HttpUtils.url(config().getHost(), url);
    }

    /**
     * 获取请求路径 有Get请求参数
     */
    public String getQueryUrl() {
        String url = getUrl();
        // 处理请求地址
        List<HttpParam> queryParameters = getQueryParameters();
        if (!queryParameters.isEmpty()) {
            url += "?" + HttpUtils.urlParam(queryParameters);
        }
        return url;
    }

    public int getRetryCount() {
        if (retryCount == 0) {
            return config().getRetryCount();
        }
        return retryCount;
    }

    public T retryCount(int retryCount) {
        this.retryCount = retryCount;
        return (T) this;
    }

    //<editor-fold desc="Download">

    public boolean isDownload() {
        return HttpMethod.DOWNLOAD.equals(method);
    }

    public boolean isGet() {
        return HttpMethod.GET.equals(method) || isDownload();
    }

    /**
     * 添加下载进度监听
     *
     * @param listener 监听事件
     */
    public T addDownloadListener(ProgressListener listener) {
        if (!downloadProgressListeners.contains(listener)) {
            downloadProgressListeners.add(listener);
        }
        return (T) this;
    }

    public HttpTag.DownloadListeners downloadProgressListeners() {
        return downloadProgressListeners;
    }
    //</editor-fold>

    //<editor-fold desc="timeout">
    public int getConnectTimeout() {
        if (connectTimeout == null) {
            return config().getConnectTimeout();
        }
        return connectTimeout;
    }

    public int getReadTimeout() {
        if (readTimeout == null) {
            return config().getReadTimeout();
        }
        return readTimeout;
    }

    public int getWriteTimeout() {
        if (writeTimeout == null) {
            return config().getWriteTimeout();
        }
        return writeTimeout;
    }

    public T timeout(int connectTimeout, int readTimeout, int writeTimeout) {
        this.writeTimeout = writeTimeout;
        this.readTimeout = readTimeout;
        this.connectTimeout = connectTimeout;
        return (T) this;
    }
    //</editor-fold>

    //<editor-fold desc="Param">
    public T param(String k, Object v) {
        bodyParameters.add(new HttpParam(k, v));
        return (T) this;
    }

    @Override
    public T param(String k, Object v, boolean enabled) {
        this.bodyParameters.add(new HttpParam(k, v, enabled));
        return (T) this;
    }

    public T paramAll(Map<String, Object> params) {
        if (params != null) {
            this.bodyParameters.addAll(HttpParam.create(params));
        }
        return (T) this;
    }

    @Override
    public T paramAll(List<HttpParam> params) {
        if (params != null) {
            this.bodyParameters.addAll(params);
        }
        return (T) this;
    }

    public List<HttpParam> getBodyParameters() {
        return bodyParameters;
    }
    //</editor-fold>

    //<editor-fold desc="Query">
    public T query(String k, Object v) {
        this.queryParameters.add(new HttpParam(k, v));
        return (T) this;
    }

    @Override
    public T query(String k, Object v, boolean enabled) {
        this.queryParameters.add(new HttpParam(k, v, enabled));
        return (T) this;
    }

    public T addQueryAll(Map<String, Object> query) {
        if (query != null) {
            this.queryParameters.addAll(HttpParam.create(query));
        }
        return (T) this;
    }

    @Override
    public T addQueryAll(List<HttpParam> query) {
        if (query != null) {
            this.queryParameters.addAll(query);
        }
        return (T) this;
    }

    public List<HttpParam> getQueryParameters() {
        return queryParameters;
    }
    //</editor-fold>

    //<editor-fold desc="Header">
    ///添加header
    public T header(String k, String v) {
        header.put(k, v);
        return (T) this;
    }

    ///添加header
    public T addHeaderAll(Map<String, String> header) {
        if (header != null) {
            this.header.putAll(header);
        }
        return (T) this;
    }

    public Map<String, String> getHeader() {
        return header;
    }

    ///移除header
    public T removeHeader(String k) {
        header.remove(k);
        return (T) this;
    }
    //</editor-fold>

    //<editor-fold desc="Interceptor">
    public List<HttpInterceptor.RequestInterceptor> getRequestInterceptor() {
        return requestInterceptor;
    }

    public T interceptor(HttpInterceptor.RequestInterceptor requestInterceptor) {
        if (requestInterceptor != null && !this.requestInterceptor.contains(requestInterceptor)) {
            this.requestInterceptor.add(requestInterceptor);
        }
        return (T) this;
    }

    public List<HttpInterceptor.ResponseInterceptor> getResponseInterceptor() {
        return responseInterceptor;
    }

    public T interceptor(HttpInterceptor.ResponseInterceptor responseInterceptor) {
        if (responseInterceptor != null && !this.responseInterceptor.contains(responseInterceptor)) {
            this.responseInterceptor.add(responseInterceptor);
        }
        return (T) this;
    }

    public List<HttpInterceptor.ExceptionInterceptor> getExceptionInterceptor() {
        return exceptionInterceptor;
    }

    public T interceptor(HttpInterceptor.ExceptionInterceptor exceptionInterceptor) {
        if (exceptionInterceptor != null && !this.exceptionInterceptor.contains(exceptionInterceptor)) {
            this.exceptionInterceptor.add(exceptionInterceptor);
        }
        return (T) this;
    }

    public HttpConvert<?> getHttpConvert() {
        if (httpConvert == null) {
            httpConvert = config().getHttpConvert();
        }
        return httpConvert;
    }

    public T convert(HttpConvert<?> httpConvert) {
        this.httpConvert = httpConvert;
        return (T) this;
    }
    //</editor-fold>

    /**
     * 获取缓存的Key
     *
     * @return
     */
    public String getCacheKey() {
        String url = getUrl();
        String method = getMethod();
        // 处理请求地址
        List<HttpParam> queryParameters = getQueryParameters();
        if (!queryParameters.isEmpty()) {
            url += "?" + HttpUtils.urlParam(queryParameters);
        }
        List<HttpParam> bodyParameters = getBodyParameters();
        if (!bodyParameters.isEmpty()) {
            url += "?" + HttpUtils.urlParam(bodyParameters);
        }
        url += method;
        return HttpFileUtils.md5String(url, false);
    }

    public void clear() {
        url = null;
        retryCount = 0;
        connectTimeout = null;
        readTimeout = null;
        writeTimeout = null;
        header.clear();
        queryParameters.clear();
        bodyParameters.clear();
        requestInterceptor.clear();
        responseInterceptor.clear();
        exceptionInterceptor.clear();
        downloadProgressListeners.clear();
        httpConvert = null;
        type = null;
    }

    protected void check() {
        if (!config().header().isEmpty()) {
            header.putAll(config().header());
        }
        if (!config().query().isEmpty()) {
            queryParameters.addAll(config().query());
        }
        if (!config().params().isEmpty()) {
            bodyParameters.addAll(config().params());
        }
        if (config().getRequestInterceptor() != null) {
            requestInterceptor.add(config().getRequestInterceptor());
        }
        if (config().getResponseInterceptor() != null) {
            responseInterceptor.add(config().getResponseInterceptor());
        }
        if (config().getExceptionInterceptor() != null) {
            exceptionInterceptor.add(config().getExceptionInterceptor());
        }
    }

    @Override
    public <D> D execute(Type type) throws IOException {
        setType(type);
        return execute();
    }

    /**
     * 开始请求
     */
    @Override
    public <D> D execute() throws IOException {
        if (!HttpUtils.isNetworking(HttpConfig.config().getContext())) {
            throw HttpException.throwNetwork(getUrl());
        }
        showDialog();
        HttpCall.Factory factory = config().getFactory();
        try {
            HttpLogUtils.requestLog(this, "Request");
            for (HttpInterceptor.RequestInterceptor interceptor : requestInterceptor) {
                interceptor.intercept(this);
            }
            HttpCall<?> newCall = factory.newCall(this);
            Object response = newCall.execute();
            return (D) HttpUtils.handleSuccess(this, response);
        } catch (IOException e) {
            HttpUtils.handleFailed(HttpRequest.this, e);
            throw e;
        } finally {
            dismissDialog();
        }
    }

    /**
     * 开始请求
     */
    @Override
    public <D> void enqueue(final HttpCallback<D> callback) {
        checkType(callback.getClass());
        showDialog();
        if (HttpConfig.isMainThread()) {
            HttpConfig.runIo(new Runnable() {
                @Override
                public void run() {
                    enqueue(callback);
                }
            });
            return;
        }
        // 运行在工作线程
        if (!HttpUtils.isNetworking(HttpConfig.config().getContext())) {
            onEnqueueFailed(callback, HttpException.throwNetwork(getUrl()));
            return;
        }
        HttpLogUtils.requestLog(this, "Request");
        final HttpCall.Factory factory = config().getFactory();
        for (HttpInterceptor.RequestInterceptor interceptor : requestInterceptor) {
            interceptor.intercept(this);
        }
        final HttpCall<Object> newCall = factory.newCall(this);
        HttpConfig.runUI(new Runnable() {
            @Override
            public void run() {
                enqueueReal(newCall, callback);
            }
        });
    }


    private <D> void enqueueReal(HttpCall<Object> newCall, final HttpCallback<D> callback) {
        // 需要在主线程请求
        newCall.enqueue(new HttpCallback<Object>() {
            @Override
            public void onSuccess(HttpRequest<?> request, final Object response) {
                if (HttpConfig.isMainThread()) {
                    HttpConfig.runIo(new Runnable() {
                        @Override
                        public void run() {
                            onSuccess(request, response);
                        }
                    });
                    return;
                }
                onEnqueueResponse(request, response, callback);
            }

            @Override
            public void onFailed(Throwable throwable) {
                onEnqueueFailed(callback, throwable);
            }
        });
    }

    private <D> void onEnqueueFailed(final HttpCallback<D> callback, Throwable throwable) {
        HttpConfig.runIo(new Runnable() {
            @Override
            public void run() {
                HttpUtils.handleFailed(HttpRequest.this, throwable);
                callFailed(callback, throwable);
            }
        });
    }

    private <D> void onEnqueueResponse(HttpRequest<?> request, Object response, final HttpCallback<D> callback) {
        try {
            callSuccess(request, callback, (D) HttpUtils.handleSuccess(request, response));
        } catch (HttpException e) {
            onEnqueueFailed(callback, e);
        }
    }

    private <D> void callFailed(HttpCallback<D> callback, Throwable throwable) {
        dismissDialog();
        config().getCallBack().execute(() -> {
            if (callback != null) {
                callback.onFailed(throwable);
            }
        });
    }

    private <D> void callSuccess(HttpRequest<?> request, HttpCallback<D> callback, D response) {
        dismissDialog();
        config().getCallBack().execute(() -> {
            if (callback != null) {
                callback.onSuccess(request, response);
            }
        });
    }

    /**
     * 显示对话框
     */
    private void showDialog() {
        if (HttpConfig.isMainThread()) {
            if (showDialog) {
                if (dialog != null && !dialog.isShowing()) {
                    dialog.show();
                }
            }
        } else {
            if (showDialog) {
                HttpConfig.runUI(new Runnable() {
                    @Override
                    public void run() {
                        if (dialog != null && !dialog.isShowing()) {
                            dialog.show();
                        }
                    }
                });
            }
        }

    }

    /**
     * 加载框消失
     */
    private void dismissDialog() {
        if (HttpConfig.isMainThread()) {
            if (showDialog) {
                showDialog = false;
                if (dialog != null && dialog.isShowing()) {
                    dialog.dismiss();
                    dialog = null;
                }
            }
        } else {
            if (showDialog) {
                showDialog = false;
                HttpConfig.runUI(() -> {
                    if (dialog != null && dialog.isShowing()) {
                        dialog.dismiss();
                        dialog = null;
                    }
                });
            }
        }
    }

    public T setType(Type type) {
        if (type == null) {
            return (T) this;
        }
        this.type = type;
        return (T) this;
    }

    public Type getType() {
        return type;
    }

    private <D> void checkType(Class<?> callback) {
//        if (type != null) {
//            return;
//        }
        if (callback != null) {
            Type genType = callback.getGenericSuperclass();
            if (genType != null) {
                type = ((ParameterizedType) genType).getActualTypeArguments()[0];
            }
            if (type == null) {
                throw new RuntimeException("type is not null");
            }
            return;
        }
    }

    protected HttpConfig config() {
        return HttpConfig.config();
    }
}
